package com.zhtools.tree.service.impl;

import com.zhtools.tree.TreeNode;
import com.zhtools.tree.service.TreeService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by zhanghao on 2017/7/27.
 */
public abstract class AbstractTreeService<T extends TreeNode> implements TreeService<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTreeService.class);
    private static final Long ROOT = 0L;

    /**
     * 构建一颗完整的树
     *
     * @param t 节点
     * @return 节点
     */
    @Override
    public T buildTree(T t) {
        List<T> children = getChilds(t);
        if (children != null && children.size() > 0) {
            t.setChildren(children);
            for (T var : children) {
                buildTree(var);
            }
        }
        return t;
    }

    /**
     * 新增子节点
     *
     * @param t 节点
     */
    @Override
    public void addChild(T t) {
        //插入数据库
        T parent = findById(t.getParentId());
        t.setParentId(parent == null ? ROOT : parent.getId());
        insert(t);
        LOGGER.info(String.format(">>> 创建节点,id:%s", t.getId()));
        //更新path
        String path = "," + t.getId() + ",";
        if (parent != null) {
            path = parent.getPath() + t.getId() + ",";
        }
        t.setPath(path);
        update(t);
        LOGGER.info(String.format(">>> 设置节点路径,id:%s,path:%s", t.getId(), t.getPath()));
        //获取当前节点的父节点集合
        List<T> parents = getParents(t);
        if (parents == null || parents.size() == 0) {
            return;
        }
        parents = reverseOrder(parents);
        //初始化关系
        int level = 1;
        for (T p : parents) {
            buildReleation(t.getId(), p.getId(), level);
            LOGGER.info(String.format(">>> 新增关系,pId:%s,id:%s,level:%s", p.getId(), t.getId(), level));
            level = level + 1;
        }
    }

    @Override
    public void cut(T t) {
        T oldParent = findById(t.getParentId());
        final List<Long> oldParentIdList = new ArrayList<>();
        for (String str : StringUtils.split(oldParent.getPath(), ",")) {
            oldParentIdList.add(Long.parseLong(str));
        }
        Collections.reverse(oldParentIdList);

        //更新数据库
        t.setParentId(ROOT);
        t.setPath("," + t.getId() + ",");
        update(t);
        LOGGER.info(String.format(">>> 断开节点%s与节点%s的关系", t.getId(), oldParent.getId()));
        //刷新path和删除与上层的关系
        this.traversal(t, new TraversalHandler<T>() {
            @Override
            public void process(T node) {
                //刷新path
                if (node.getParentId() != ROOT) {
                    T parent = findById(node.getParentId());
                    node.setPath(parent.getPath() + node.getId() + ",");
                    update(node);
                    LOGGER.info(">>> 刷新节点%s路径%s", node.getId(), node.getPath());
                }
                //删除旧关系
                for (int i = 0; i < oldParentIdList.size(); i++) {
                    Long pId = oldParentIdList.get(i);
                    Integer level = node.deep() + i + 1;
                    removeReleation(node.getId(), pId, level);
                    LOGGER.info(String.format(">>> 删除节点%s相对节点%s第%s代的关系", node.getId(), pId, level));
                }
            }
        });
    }

    @Override
    public void changeParent(T node, T parent) {
        T oldParent = findById(node.getParentId());
        final List<Long> oldParentIdList = new ArrayList<>();
        if (oldParent != null) {
            for (String str : StringUtils.split(oldParent.getPath(), ",")) {
                oldParentIdList.add(Long.parseLong(str));
            }
            Collections.reverse(oldParentIdList);
        }

        final List<Long> parentIdList = new ArrayList<>();
        if (parent != null) {
            for (String str : StringUtils.split(parent.getPath(), ",")) {
                parentIdList.add(Long.parseLong(str));
            }
            Collections.reverse(parentIdList);
        }

        //更新数据库
        node.setParentId(parent.getId());
        update(node);
        LOGGER.info(String.format(">>> 更换节点%s的父节点", node.getId()));

        //刷新path维护关系
        this.traversal(node, new TraversalHandler<T>() {
            @Override
            public void process(T node) {
                //刷新path
                T parent = findById(node.getParentId());
                node.setPath(parent.getPath() + node.getId() + ",");
                update(node);
                LOGGER.info(">>> 刷新节点%s路径%s", node.getId(), node.getPath());
                //删除旧关系
                for (int i = 0; i < oldParentIdList.size(); i++) {
                    Long pId = oldParentIdList.get(i);
                    Integer level = node.deep() + i + 1;
                    removeReleation(node.getId(), pId, level);
                    LOGGER.info(String.format(">>> 删除节点%s相对节点%s第%s代的关系", node.getId(), pId, level));
                }
                //新增新关系
                for (int i = 0; i < parentIdList.size(); i++) {
                    Long pId = parentIdList.get(i);
                    Integer level = node.deep() + i + 1;
                    buildReleation(node.getId(), pId, level);
                    LOGGER.info(String.format(">>> 新增节点%s相对节点%s第%s代的关系", node.getId(), pId, level));
                }
            }
        });
    }

    private List<T> getParents(T t) {
        String path = t.getPath();
        String[] idStrs = StringUtils.split(path, ",");
        List<Long> ids = new ArrayList<>();
        for (String idStr : idStrs) {
            Long id = Long.parseLong(idStr);
            if (id.longValue() != t.getId().longValue()) {
                ids.add(id);
            }
        }
        return queryByIds(ids);
    }

    private List<T> reverseOrder(List<T> list) {
        List<T> copyList = new ArrayList<>(list);
        Collections.reverse(copyList);
        return copyList;
    }

    private void traversal(T t, TraversalHandler<T> handler) {
        LinkedList<T> list = new LinkedList<>();
        list.add(t);
        while (!list.isEmpty()) {
            T node = list.pollFirst();
            LOGGER.debug(String.format(">>> 遍历节点%s,代数%s", node.getId(), node.deep()));
            if (handler != null) {
                LOGGER.debug(String.format(">>> 处理节点%s,代数%s", node.getId(), node.deep()));
                handler.process(node);
            }
            List<T> children = getChilds(node);
            if (children != null && children.size() > 0) {
                for (T loop : children) {
                    loop.nextDeep();
                    list.add(loop);
                }
            }
        }
    }

    protected abstract List<T> queryByIds(List<Long> ids);

    protected abstract T findById(Long id);

    protected abstract Long insert(T t);

    protected abstract Integer update(T t);

    /**
     * 建立关系
     *
     * @param nodeId 当前节点编号
     * @param pId    父节点编号
     * @param level  当前节点相对父节点的代数
     */
    protected abstract void buildReleation(Long nodeId, Long pId, Integer level);

    /**
     * 移除关系
     *
     * @param nodeId 当前节点编号
     * @param pId    父节点编号
     * @param level  当前节点相对父节点的代数
     */
    protected abstract void removeReleation(Long nodeId, Long pId, Integer level);

    public static interface TraversalHandler<T extends TreeNode> {
        void process(T node);
    }
}
